/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://gitee.com/newgolo/embedme.git
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#ifndef __CONFIG_H__
#define __CONFIG_H__

#include <vector>
#ifdef OS_ANDROID
#include "libconfigcpp.h"
#else
#include "libconfig.h++"
#endif
#include "libemb/BaseType.h"
#include "libemb/Tracer.h"
#include "libemb/ThreadUtil.h"


/**
 * @file Config.h
 * @brief 配置文件
 * @code Config Brief Introduce
 * 配置文件的数据类型及格式:
 * 用大括号"{}"表示一组配置
 * 用分号";"隔开配置项
 * 同时支持C/C++/脚本三种风格的注释
 *---------------------------------------------------------------------
 *  int = 255;                      //整形值,也可以用16进制表示:0xFF
 *  double = 3.1415926;             //浮点值
 *  string = "Jim";                 //字符串
 *  int_array = [1,-1,1,0];         //数组,用[]表示,元素之间用逗号隔开
 *  double_array = [1.01, 2.02, 3.03];
 *  string_array = ["one","two","three"];
 *  list =                          //列表,用()包含,元素之间用逗号隔开
 * (                          
 *      {item=1;},                  //list[0],配置项之间用分号隔开
 *      {item=2;name="Tom";},       //list[1],列表元素间用逗号隔开
 *  ); 
 * group:                          //组配置,用{}包含
 *  {
 *     string ="hello";           ###配置项之间用分号隔开
 *     subgroup:
 *      {
 *         array=[1,2,3,4];
 *     }
 * };
 *---------------------------------------------------------------------
 *  整个文件是一个Setting,下面包含一个或多个Setting
 *  每个Setting里可以包含若干子Setting和其他数据类型
 * @endcode
 */
using namespace std;
using namespace libemb;

namespace libembx{
/**
 *  @file   Config.h   
 *  @class  Settings
 *  @brief  配置项	
 */
class Config;

/**
 * @class Settings
 * @brief 设置项
 */
class Settings{
DECL_CLASSNAME(Settings)
public:
    Settings(){};
    Settings(libconfig::Setting* root);
    Settings(const Settings&);
    virtual ~Settings();
    Settings operator[](int idx);
    Settings operator[](std::string name);
    bool exists(std::string name);
	std::string name();
    int size();
    int toInt();
    double toDouble();
    std::string toString();
    libemb::IntArray toIntArray();
    libemb::DoubleArray toDoubleArray();
    libemb::StringArray toStringArray();
    bool addInt(const std::string& keyname,int value=0);
    bool addDouble(const std::string& keyname,double value=0);
    bool addString(const std::string& keyname,const string& value="");
	template <typename T>
    bool addArray(const std::string& keyword,libemb::Array<T>& array)
	{
	    if (keyword.empty())
	    {
	        TRACE_ERR_CLASS("keyword is empty!\n");
	        return false;
	    }
	    if (m_root!=NULL)
	    {
	        if (m_root->exists(keyword))
	        {
	            TRACE_ERR_CLASS("Setting[%s] is exist!\n",CSTR(keyword));
	            return false;
	        }
	        else
	        {
	            /* 当根结点是组时或者直接就是根配置(此时m_root非组,m_keyword为空)时,直接增加 */
	            if (m_root->getType()==libconfig::Setting::TypeGroup || m_keyword.empty())
	            {
	                m_root->add(keyword,libconfig::Setting::TypeArray);
	                for(auto i=0; i<array.size(); i++)
	                {
	                    switch(array.type())
	                    {
	                        case BASETYPE_INTARRAY:
	                             (*m_root)[keyword].add(libconfig::Setting::TypeInt);
	                             break;
	                        case BASETYPE_DOUBLEARRAY:
	                             (*m_root)[keyword].add(libconfig::Setting::TypeFloat);
	                             break;
	                        case BASETYPE_STRINGARRAY:
	                             (*m_root)[keyword].add(libconfig::Setting::TypeString);
	                             break;
	                        default:
	                            return false;
	                    }
						(*m_root)[keyword][i] = array[i];
	                }
	                return true;
	            }
	            else
	            {
	                switch ((*m_root)[m_keyword].getType())
	                {
	                    case libconfig::Setting::TypeGroup:     /* 6: 配置组 */
	                        break;
	                    default:
	                        TRACE_ERR_CLASS("Setting[%s] is not group!\n",CSTR(m_keyword));
	                        return false;
	                }
	                (*m_root)[m_keyword].add(keyword,libconfig::Setting::TypeArray);
	                for(auto i=0; i<array.size(); i++)
	                {
	                	switch(array.type())
	                    {
	                        case BASETYPE_INTARRAY:
	                             (*m_root)[keyword].add(libconfig::Setting::TypeInt);
	                             break;
	                        case BASETYPE_DOUBLEARRAY:
	                             (*m_root)[keyword].add(libconfig::Setting::TypeFloat);
	                             break;
	                        case BASETYPE_STRINGARRAY:
	                             (*m_root)[keyword].add(libconfig::Setting::TypeString);
	                             break;
	                        default:
	                            return false;
	                    }
						(*m_root)[keyword][i] = array[i];
	                }
	                return true;
	            }
	        }
	    }
	    else
	    {
	        TRACE_ERR_CLASS("Null Setting!\n");
	    }
	    return false;  
	}
    bool addList(const std::string& keyname);
    bool addGroup(const std::string& keyname="");
    Settings& operator=(const int& value);
    Settings& operator=(const double& value);
    Settings& operator=(const std::string& value);
    Settings& operator=(libemb::IntArray& value);
    Settings& operator=(libemb::DoubleArray& value);
private:
    void setRoot(libconfig::Setting* root);
private:
    friend class Config;
    libconfig::Setting* m_root{NULL};
    std::string m_keyword{""};
};

/**
 * @class Config
 * @brief 配置类	
 */
class Config{
DECL_CLASSNAME(Config)
public:
    Config();
    ~Config();
    bool initWithFile(const std::string& cfgFile);
    bool saveAsFile(const std::string& cfgFile);
    Settings operator[](std::string keyword);
    Settings rootSettings();
private:
    std::string m_filePath;
    libconfig::Setting* m_rootSetting{NULL};
	std::unique_ptr<libconfig::Config> m_config;
    std::unique_ptr<Settings> m_setting;
    std::unique_ptr<Settings> m_nullsetting;
};
}

#endif
